﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Model;

namespace ReportBrowser.ViewModel
{
    internal class MemberViewModel : LanguageElementViewModelBase
    {
        #region Fields

        private static Dictionary<Member, MemberViewModel> sCallerInstances =
            new Dictionary<Member, MemberViewModel>();
        private static Dictionary<Member, MemberViewModel> sCalleeInstances =
            new Dictionary<Member, MemberViewModel>();

        #endregion Fields

        #region Lifecycle

        static MemberViewModel()
        {
            MessageViewModel.InstanceSelected += new EventHandler(MessageViewModel_InstanceSelected);
        }

        public MemberViewModel(Member model, bool isTruncatedAsCaller = false, bool isTruncatedAsCallee = false)
        {
            this.Model = model;
            this.Callees = new ObservableCollection<MemberViewModel>();
            this.Callers = new ObservableCollection<MemberViewModel>();
        }

        public static MemberViewModel GetInstance(Member model, bool isCaller)
        {
            if (isCaller == true)
            {
                if (sCallerInstances.ContainsKey(model) == false)
                    sCallerInstances.Add(model, new MemberViewModel(model));
                return sCallerInstances[model];
            }
            else if (isCaller == false)
            {
                if (sCalleeInstances.ContainsKey(model) == false)
                    sCalleeInstances.Add(model, new MemberViewModel(model));
                return sCalleeInstances[model];
            }
            else
                throw new ArgumentException();
        }

        public static bool IsInstantiated(Member model, bool isCaller)
        {
            if (isCaller == true)
                return sCallerInstances.ContainsKey(model);
            else if (isCaller == false)
                return sCalleeInstances.ContainsKey(model);
            else
                throw new ArgumentException();
        }

        private static void ClearInstances()
        {
            sCallerInstances = new Dictionary<Member, MemberViewModel>();
            sCalleeInstances = new Dictionary<Member, MemberViewModel>();
        }

        #endregion Lifecycle

        #region Properties

        public Member Model { get; private set; }

        private bool mIsCallersInitialized = false;
        private ObservableCollection<MemberViewModel> pCallers;
        public ObservableCollection<MemberViewModel> Callers
        {
            get
            {
                if (mIsCallersInitialized == false)
                {
                    foreach (var item in this.Model.CallGraphEdge_MemberDependency_Callee)
                    {

                        if (item.CallerMember != null)
                            pCallers.Add(GetInstance(item.CallerMember, isCaller: true));
                        else
                            Debug.WriteLine("(item in CallGraphEdge_MemberDependency_Callee).CallerMember == null");
                    }
                    mIsCallersInitialized = true;
                }
                return pCallers;
            }
            private set
            {
                if (value != pCallers)
                {
                    pCallers = value;
                    OnPropertyChanged("Callers");
                }
            }
        }

        private bool mIsCalleesInitialized = false;
        private ObservableCollection<MemberViewModel> pCallees;
        public ObservableCollection<MemberViewModel> Callees
        {
            get
            {
                if (mIsCalleesInitialized == false)
                {
                    foreach (var item in this.Model.CallGraphEdge_MemberDependency_Caller)
                    {

                        if (item.CalleeMember != null)
                            pCallees.Add(GetInstance(item.CalleeMember, isCaller: false));
                        else
                            Debug.WriteLine("(item in CallGraphEdge_MemberDependency_Caller).CalleeMember == null");
                    }
                    mIsCalleesInitialized = true;
                }
                return pCallees;
            }
            private set
            {
                if (value != pCallees)
                {
                    pCallees = value;
                    OnPropertyChanged("Callees");
                }
            }
        }

        #endregion Properties

        #region Methods

        /*
        private bool IsOnCycle(Member member, bool asCaller)
        {
            HashSet<Member> gray = new HashSet<Member>();
            HashSet<Member> black = new HashSet<Member>();
            Debug.WriteLine("IsOnCycle: member=" + member.Name + " asCaller=" + asCaller.ToString());
            List<Member> path = DFS(member, asCaller, ref gray, ref black);
            //if (path != null)
            //    for (int i = 1; i < path.Count; i++)
            //    {
            //        if (path[i].Equals(path[0]) == true)
            //            return false;
            //        else if (path[i].Equals(member))
            //            return true;
            //    }
            //return false;
            if (path != null)
                Debug.WriteLine("path==<" + SetToString(path) + ">");
            return path != null;
        }

        private List<Member> DFS(Member member, bool asCaller,
            ref HashSet<Member> gray, ref HashSet<Member> black)
        {
            Debug.Indent();
            gray.Add(member);
            Debug.WriteLine("gray: {" + SetToString(gray) + "}");
            EntityCollection<CallGraphEdge_MemberDependency> neighbours;
            if (asCaller == true)
                neighbours = member.CallGraphEdge_MemberDependency_Caller;
            else
                neighbours = member.CallGraphEdge_MemberDependency_Callee;
            Debug.Indent();
            foreach (var item in neighbours)
            {
                Member neighbour;
                if (asCaller == true)
                    neighbour = item.CalleeMember;
                else
                    neighbour = item.CallerMember;
                if ((asCaller == true
                    && (MemberViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: false) == true
                            || MemberViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: true, isTruncatedAsCallee: false) == true))
                    || (asCaller == false
                        && (MemberViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: false) == true
                            || MemberViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: true) == true)))
                {
                    Debug.WriteLine("neighb: " + neighbour.Name);
                    if (gray.Contains(neighbour) == false && black.Contains(neighbour) == false)//white
                    {
                        Debug.WriteLine(" is WHITE");
                        List<Member> path = DFS(neighbour, asCaller, ref gray, ref black);
                        if (path != null)
                        {
                            Debug.WriteLine("but path=={" + SetToString(path) + "}!=null");
                            path.Add(neighbour);
                            Debug.Unindent();
                            Debug.Unindent();
                            return path;
                        }
                    }
                    else if (gray.Contains(neighbour) == true && black.Contains(neighbour) == false)//gray
                    {
                        Debug.WriteLine(" is GRAY");
                        Debug.Unindent();
                        Debug.Unindent();
                        return new List<Member>() { neighbour };
                    }
                }
            }
            Debug.Unindent();
            gray.Remove(member);
            black.Add(member);
            Debug.WriteLine("gray: {" + SetToString(gray) + "}");
            Debug.WriteLine("black:{" + SetToString(black) + "}");
            Debug.Unindent();
            return null;
        }

        private string SetToString(IEnumerable<Member> set)
        {
            string s = "";
            foreach (var item in set)
            {
                s += item.Name + ", ";
            }
            return s.TrimEnd(new char[] { ' ', ',' });
        }
        */

        #endregion Methods

        #region EventHandlers

        static void MessageViewModel_InstanceSelected(object sender, EventArgs e)
        {
            ClearInstances();
        }

        #endregion EventHandlers
    }
}
